/*
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

   Based on the source code of PelDet written by Danzel A.
   */

#include <pspkernel.h>
#include <psppower.h>
#include <pspctrl.h>
#include <pspwlan.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <SDL.h>

#include "psp_global.h"
#include "psp_sdl.h"
#include "psp_menu.h"
#include "psp_danzeff.h"
#include "psp_fmgr.h"
#include "psp_ssh.h"

#include "psp_vt100.h"

# define VT100_MAX_LINE       400
# define VT100_MAX_WIDTH       80

typedef unsigned char bool;

# define false      0
# define true       1

#define VT100_DEFAULT_TOP_Y     0
#define VT100_DEFAULT_BOTTOM_Y  (VT100_SCREEN_HEIGHT-1)

# define COLOR_BLACK       0
# define COLOR_RED         1
# define COLOR_GREEN       2
# define COLOR_YELLOW      3
# define COLOR_BLUE        4
# define COLOR_MAGENTA     5
# define COLOR_CYAN        6
# define COLOR_WHITE       7
# define VT100_MAX_COLOR   8

# define BRIGHTNESS_NORMAL  0
# define BRIGHTNESS_BRIGHT  1
# define BRIGHTNESS_DIM     2

# define FONT_DEFAULT       0
# define FONT_LINEDRAW      1

typedef struct vt100_elem_t {
  u8    m_char;
  u8    m_reverse;
  u8    m_brightness;
  u8    m_underlined;
  u8    m_blink;
  u8    m_font;
  u8    bg_color;
  u8    fg_color;
} vt100_elem_t;

# define VT100_SCREEN_TOP_Y  (VT100_MAX_LINE - VT100_SCREEN_HEIGHT)

/* vt100 screen */
static vt100_elem_t vt100_screen[VT100_MAX_LINE][VT100_MAX_WIDTH];
static int          vt100_colors[VT100_MAX_COLOR][3]; /* Bright */

static int  vt100_display_from_y = 0; 

static bool wontEcho = false;
static bool cygwinHacksMode = false;  //if we detect connecting to cygwin this is set
//if this is set then backspace is sent of \b instead of \177. other telnet clients dont need to do this but i do :(

//colors and attributes of text
static int  loc_curr_fg_color = COLOR_WHITE;
static int  loc_curr_bg_color = COLOR_BLACK;
static bool loc_curr_reverse = false;
static int  loc_curr_brightness = BRIGHTNESS_NORMAL;
static int  loc_curr_underlined = 0;
static int  loc_curr_blink = 0;

static int  loc_visual_bell = 0;

# define BLINK_MAX_COUNTER     20
static int  loc_blink_counter = 0;

//what the fonts are set as and what one we are currently using
static bool G0_font = FONT_DEFAULT;
static bool G1_font = FONT_LINEDRAW;
static bool using_gX = 0;

//saved ones (from an esc sequence)
static int  loc_saved_fg_color;
static int  loc_saved_bg_color;
static bool loc_saved_reverse;
static int  loc_saved_brightness;
static int  loc_saved_underlined;
static int  loc_saved_blink;

static bool saved_G0_font;
static bool saved_G1_font;
static bool saved_using_gX;

static int loc_curr_x;
static int loc_curr_y;
static int loc_saved_x;
static int loc_saved_y;
static u8  loc_curr_font;

static int loc_top_y    = VT100_DEFAULT_TOP_Y;
static int loc_bottom_y = VT100_DEFAULT_BOTTOM_Y;

static int loc_autowrap = 0;
//cache for replies to network
static char networkBuf[200];
static int  networkBufSize = 0;

  static void 
networkBufAdd(char* data, int len)
{
  int a;
  for (a = 0; a < len; a++)
  {
    networkBuf[a+networkBufSize] = data[a];
  }
  networkBufSize += len;
}

//input set things
#define APPLICATION_MODE false /* for both */

#define POSITIONING_MODE true
static bool cursorMode = POSITIONING_MODE;

#define NUMERIC_MODE true
static bool keypadMode = NUMERIC_MODE;
/*
 *  ESC [ ? 1 h       cursor keys in applications mode
 *  ESC [ ? 1 l       cursor keys in cursor positioning mode
 *  ESC =             keypad keys in applications mode
 *  ESC >             keypad keys in numeric mode
 */

static bool remoteEcho = false;  //if true the remote computer will echo back input, so we dont need to

  static void
psp_vt100_clear_line(int y)
{
  int x;
  for (x = 0; x < VT100_SCREEN_WIDTH; x++) {
    vt100_elem_t *elem = &vt100_screen[VT100_SCREEN_TOP_Y + y][x];
    elem->m_char = ' ';
    elem->m_font = loc_curr_font;
    elem->m_reverse = loc_curr_reverse;
    elem->m_brightness = loc_curr_brightness;
    elem->m_underlined = loc_curr_underlined;
    elem->m_blink      = loc_curr_blink;
    elem->bg_color = loc_curr_bg_color;
    elem->fg_color = loc_curr_fg_color;
  }
}


  static void
psp_vt100_shift_up()
{
  int y;
  if (loc_top_y == 0) {	//if we're scrolling off the top of the screen then scroll the back buffer
    for (y = 0; y <= (VT100_MAX_LINE-1-VT100_SCREEN_HEIGHT); y++) {
      vt100_elem_t *scan_line1 = &vt100_screen[y    ][0];
      vt100_elem_t *scan_line2 = &vt100_screen[y + 1][0];
      memcpy(scan_line1, scan_line2, sizeof(vt100_elem_t) * VT100_MAX_WIDTH);
    }
  }
  //now scroll the scroll area
  for (y = loc_top_y; y < loc_bottom_y; y++) {
    vt100_elem_t *scan_line1 = &vt100_screen[VT100_SCREEN_TOP_Y + y    ][0];
    vt100_elem_t *scan_line2 = &vt100_screen[VT100_SCREEN_TOP_Y + y + 1][0];
    memcpy(scan_line1, scan_line2, sizeof(vt100_elem_t) * VT100_MAX_WIDTH);
  }
  psp_vt100_clear_line(y);
  loc_curr_y--;
}

  static void
psp_vt100_shift_down()
{
  int y;
  for (y = loc_bottom_y; y > loc_top_y; y--) {
    vt100_elem_t *scan_line1 = &vt100_screen[VT100_SCREEN_TOP_Y + y    ][0];
    vt100_elem_t *scan_line2 = &vt100_screen[VT100_SCREEN_TOP_Y + y - 1][0];
    memcpy(scan_line1, scan_line2, sizeof(vt100_elem_t) * VT100_MAX_WIDTH);
  }
  psp_vt100_clear_line(y);
  loc_curr_y++;
}


  static void 
fixXandY(bool moveIfNeeded)
{
  if (loc_curr_x <                   0) loc_curr_x = 0;
  if (loc_curr_x >= VT100_SCREEN_WIDTH) loc_curr_x = VT100_SCREEN_WIDTH - 1;

  if (moveIfNeeded) {
    while (loc_curr_y < loc_top_y) {
      psp_vt100_shift_down();
    }
    while (loc_curr_y > loc_bottom_y) {
      psp_vt100_shift_up();
    }
  }
  else
  {
    if (loc_curr_y < loc_top_y   ) loc_curr_y = loc_top_y;
    if (loc_curr_y > loc_bottom_y) loc_curr_y = loc_bottom_y;
  }
}

  static void 
renderGoto(int x, int y)
{
  loc_curr_x = x;
  loc_curr_y = y;
  fixXandY(false);
}

  static void 
renderGoByReal(int x, int y, bool scroll)
{
  loc_curr_x += x;
  loc_curr_y += y;
  fixXandY(scroll);
}

  static void
renderGoByMove(int x, int y)
{
  renderGoByReal(x, y, true);
}

  static void
psp_vt100_shift_up_if_needed()
{
  if (loc_curr_y > loc_bottom_y) {
    psp_vt100_shift_up();
  }
}

  static void
vt100_print_char(char c)
{
  int index = 0;
  //line wrap if need to
  if (c == '\t') {
    index = 8;
    c = ' ';
  }

  do {

    if (loc_curr_x >= VT100_SCREEN_WIDTH) {
      loc_curr_x = 0;
      loc_curr_y++;
      psp_vt100_shift_up_if_needed();
    }
    vt100_elem_t *elem = &vt100_screen[VT100_SCREEN_TOP_Y + loc_curr_y][loc_curr_x];
    elem->m_char = c;
    elem->m_font = loc_curr_font;
    elem->m_reverse = loc_curr_reverse;
    elem->m_brightness = loc_curr_brightness;
    elem->m_underlined = loc_curr_underlined;
    elem->m_blink      = loc_curr_blink;
    elem->bg_color = loc_curr_bg_color;
    elem->fg_color = loc_curr_fg_color;
    loc_curr_x++;

  } while (--index > 0);

}

  static void
vt100_print_text(char *text)
{
  int index = 0;
  int length = strlen(text);

  for (index = 0; index < length; index++) {
    if ((text[index] < ' ') && (text[index] != '\t')) {
      switch (text[index])
      {
        case '\r':
          loc_curr_x = 0;
          break;
        case '\n':
        case '\v':	//vertical tab
          loc_curr_y++;
          psp_vt100_shift_up_if_needed();
          break;
        case '\a': //bell
          if (PSPSSH.psp_visual_bell) {
            loc_visual_bell = 5;
          }
          break;
        default:
          //LUDO: Don't print unprintable char 
# if 0 //LUDO: DEBUG
          fprintf(stdout, "%s %d: skip char %x\n", __FILE__, __LINE__, text[index]);
# endif
          break;
      }

    } else {
      vt100_print_char(text[index]);
    }
  }
}

//render text using current colors and text attributes
  static void 
styledRender(char* text)
{
  vt100_print_text(text);
}


# if 0 //LUDO:
/* A thread which implements the telnet protocol */
int telnetProtocol(SceSize args, void *argp)
{
  protocolMsg* data;
  int error;

  //send initial hello type message
  char testset[100];
  sprintf(testset, "%c%c%c" "%c%c%c" "%c%c%c" "%c%c%c" "%c%c%c" "%c%c%c" "%c%c%c" "%c%c%c", 
      0xff, 0xfd, 0x03,  //Do Suppress Go Ahead
      0xff, 0xfb, 0x18,  //Will Terminal Type
      0xff, 0xfb, 0x1f,  //Will Negotiate About Window Size
      0xff, 0xfb, 0x20,  //Will Terminal Speed
      0xff, 0xfb, 0x21,  //Will Remote Flow Control  (sorta HACK, we just ignore future messages about it)
      0xff, 0xfb, 0x22,  //Will Line Mode      (sorta HACK, we only can really handle talking to cygwin and others that behave the same)
      0xff, 0xfb, 0x27,  //Will New Environment Option (almost HACK)
      0xff, 0xfd, 0x05  //Do Status
      );
  networkSend(testset);

  setDisplayAttribute(0);//reset display
  //  renderMode(RENDER_FAST);

  while (1)
  {

    error = sceKernelReceiveMbx(protocolMessagebox, (void*)&data, NULL);
    if (error < 0)
    {
      //OH NO ERROR :(
      //tell renderer an error i guess....
      renderMain("TELNET IS DIED OHES NOES", COLOR_RED);
      return -1;
    }

    switch(data->from)
    {
      case FROM_NETWORK:
        vt100Render(data->text, data->length);

        if(networkBufSize != 0)
        {
          psp_ssh_put_data_to_ssh(networkBuf, networkBufSize);
          networkBufSize = 0;
        }
        break;
      case FROM_USER:
        {
          if (!wontEcho)
          {
            char tosend[100];
            sprintf(tosend, "%s\r\n", data->text); 
            networkSend(tosend);
            styledRender(tosend);
          }
          else
          {
            if (data->text[0] == '\n' || data->text[0] == '\r')
            {
              if (cygwinHacksMode)  //okay so this isnt actually a cygwin hack as it has been negotiated from linemode, but it is okay for now
              {
                psp_ssh_put_data_to_ssh("\r", 1);  //  (CYGWIN HACK)
              }
              else
              {
                psp_ssh_put_data_to_ssh("\r\0", 2);
              }

              if (!remoteEcho)
                styledRender("\r\n");
            }
            else
            {
              //Need to do some replacement of charcodes when sending, not sure which ;)
              switch(data->text[0])
              {
                case '\b':
                  if (cygwinHacksMode)
                  {
                    psp_ssh_put_data_to_ssh("\b", 1);
                  }
                  else
                  {
                    psp_ssh_put_data_to_ssh("\177", 1);
                  }
                  if (!remoteEcho)
                  {
                    char* forscreen = "\b \b";
                    styledRender(forscreen);
                  }
                  break;
                default:
                  psp_ssh_put_data_to_ssh(data->text, 1);
                  if (!remoteEcho)
                  {
                    char forscreen[2];
                    forscreen[0] = data->text[0];
                    forscreen[1] = '\0';
                    styledRender(forscreen);
                  }
              }

            }
          }
          break;
        }
      case FROM_USER_CODE:
        {
          char* code = "";
          int len = 0;

          switch(data->text[0])
          {
            case 33:  //page up
              code = "\033[5~";
              len = 4;
              break;
            case 34:  //page down
              code = "\033[6~";
              len = 4;
              break;
            case 35:  //end
              code = "\033[F";
              len = 3;
              break;
            case 36:  //home
              code = "\033[H";
              len = 3;
              break;
            case 37:  //left
              if (cursorMode == POSITIONING_MODE)
                code = "\033[D";
              else
                code = "\033OD";
              len = 3;
              break;
            case 38:  //up
              if (cursorMode == POSITIONING_MODE)
                code = "\033[A";
              else
                code = "\033OA";
              len = 3;
              break;
            case 39:  //right
              if (cursorMode == POSITIONING_MODE)
                code = "\033[C";
              else
                code = "\033OC";
              len = 3;
              break;
            case 40:  //down
              if (cursorMode == POSITIONING_MODE)
                code = "\033[B";
              else
                code = "\033OB";
              len = 3;
              break;
            case 45:  //Insert
              code = "\033[2~";
              len = 4;
              break;
            case 46:  //Delete
              code = "\033[3~";
              len = 4;
              break;
          }

          psp_ssh_put_data_to_ssh(code, len);
          break;
        }
    }
    free(data->text);
    free(data);
  }

  //Should never happen
  return 0;
}
# endif

//flush the torender Buffer
#define flushBuffer() {\
  styledRender(torender);\
  torender[0] = '\0';\
}

/*
   0 = normal char printing
   1 = just read escape character
   2 = Parsing from ^[

   3 = Just read a IAC (255), this means we are getting a WILL/WON'T/DO/DON'T
   4 = Read Will
   5 = Read Won't
   6 = Read Do
   7 = Read Don't
   8 = Reading Suboption

   9 = Ready for end of suboption

   10 = Parsing from ^#  (debug)
   11 = Parsing from ^(  (charset)
   12 = Parsing from ^)  (charset)
   */
static int loc_state = 0;

//int current_color = COLOR_WHITE;

char escSequence[200];
int escSequenceLen = 0;

//buffer of text to render, space for more than 2 screens of plain chars
char torender[4096];

void handleSuboption(char inputChr)
{
  if (inputChr == '\377')
  {
    loc_state = 9;
    return;
  }
  else
  {
    escSequence[escSequenceLen] = inputChr;
    escSequenceLen++;
    escSequence[escSequenceLen] = '\0';
    return;
  }
}

void handleSuboptionEnd()
{
  if (escSequence[0] == 0x18 && escSequence[1] == 0x01) //Send your terminal Type
  {
    //send suboption terminal type vt100
    //    char sendbuf[11] = { 0xff, 0xfa, 0x18, 0x00, 0x56, 0x54, 0x31, 0x30, 0x30, 0xff, 0xf0 };  //vt100
    //    char sendbuf[10] = { 0xff, 0xfa, 0x18, 0x00, 0x41, 0x4e, 0x53, 0x49, 0xff, 0xf0 }; //ansi
    char sendbuf[11] = { 0xff, 0xfa, 0x18, 0x00, 0x58, 0x54, 0x45, 0x52, 0x4d, 0xff, 0xf0 }; //xterm
    networkBufAdd(sendbuf, 11);
  } else 
    if (escSequence[0] == 0x20 && escSequence[1] == 0x01) //send your Terminal speed
    {
      char sendbuf[17] = { 0xff, 0xfa, 0x20, 0x00, 0x33, 0x38, 0x34, 0x30, 0x30, 0x2c, 0x33, 0x38, 0x34, 0x30, 0x30, 0xff, 0xf0 }; // 38400,38400 (could use something else)
      networkBufAdd(sendbuf, 17);
    } else
      if (escSequence[0] == 0x22 && escSequence[1] == 0x01 && escSequence[2] == 0x13)  //linemode 0x13
      {
        char sendbuf[10] = { 0xff, 0xfa, 0x22, 0x01, 0x17, 0xff, 0xf0 }; //linemode 0x17
        networkBufAdd(sendbuf, 10);
        cygwinHacksMode = true;   //This seems like a good enough place to set it...
      } else
        if (escSequence[0] == 0x27 && escSequence[1] == 0x01) // new environment option (1)
        {
          char sendbuf[6] = { 0xff, 0xfa, 0x27, 0x00, 0xff, 0xf0 };

          networkBufAdd(sendbuf, 6);
        }
      //TODO HANDLE OTHERS...
}

void handleWill(char inputChr)
{
  switch(inputChr)
  {
    case 0x01: //Will ECHO
      {
        remoteEcho = true;

        char Msg[3] = { 255, 253, 1 };  //Do Echo
        networkBufAdd(Msg, 3);
        break;
      }
    case 0x03: //Will Suppress Go Ahead
      {
        char Msg[3] = { 0xff, 0xfd, 0x03 };  //Do Suppress go Ahead
        networkBufAdd(Msg, 3);
        break;
      }
# if 0 //LUDO: DEBUG
    default :
      fprintf(stdout, "%s %d: skip char %x\n", __FILE__, __LINE__, inputChr);
      break;
# endif
  }
}

void handleWont(char inputChr)
{
  switch(inputChr)
  {
# if 0 //LUDO: DEBUG
    default :
      fprintf(stdout, "%s %d: skip char %x\n", __FILE__, __LINE__, inputChr);
      break;
# endif
  }
}

void handleDo(char inputChr)
{
  switch(inputChr)
  {
    //Things we should do (but dont yet)
    case 0x18: //Terminal Type
      break;  //i dont think we do anything here, only on a 'plz send term type' msg should we do anything?

      //Things we need to ignore
    case 0x20: //Terminal Speed
    case 39: //New Environment Option
      break;

      //Things we dont do and never will
    case 35: //X Display Location
    case 36: //Do environment Option
      {
        char wontMsg[3];    //wont <what you said>
        wontMsg[0] = 255;
        wontMsg[1] = 252;
        wontMsg[2] = inputChr;
        networkBufAdd(wontMsg, 3);
        break;
      }

    case 0x01: //do echo
      {
# if 0 //LUDO: ??
        renderMode(RENDER_FAST);//change render mode here to let MUD mode work right (HACK)
# endif
        wontEcho = true;
# if 0 //LUDO: ??
        renderDrawCurrentPos(true);
# endif
        char wontMsg[4];    //Wont echo
        wontMsg[0] = 255;
        wontMsg[1] = 252;
        wontMsg[2] = 1;
        networkBufAdd(wontMsg, 3);
        break;
      }
    case 0x1f: //do Negotiate about window size
      {
        char gogo[9];
        gogo[0] = 0xff;
        gogo[1] = 0xfa;
        gogo[2] = 0x1f;
        gogo[3] = 0x00;
        gogo[4] = VT100_SCREEN_HEIGHT;
        gogo[5] = 0x00;
        gogo[6] = VT100_SCREEN_WIDTH;
        gogo[7] = 0xff;
        gogo[8] = 0xf0;
        networkBufAdd(gogo, 9);
        break;
      }
    case 0x00: //do binary transmission
      {
        char willMsg[3] = { 0xff, 0xfb, 0x00 };
        networkBufAdd(willMsg, 3); //will Binary Transmission
        break;
      }
    case 0x22: //Line Mode
      {
        char lineMsg[54] = {                                              0xff, 0xfa, 0x22, 0x03, 0x01,   //05
          0x00, 0x00, 0x03, 0x62, 0x03, 0x04, 0x02, 0x0f, 0x05, 0x00, 0x00, 0x07, 0x62, 0x1c, 0x08, 0x02,    //16
          0x04, 0x09, 0x42, 0x1a, 0x0a, 0x02, 0x7f, 0x0b, 0x02, 0x15, 0x0c, 0x02, 0x17, 0x0d, 0x02, 0x12,    //16
          0x0e, 0x02, 0x16, 0x0f, 0x02, 0x11, 0x10, 0x02, 0x13, 0x11, 0x00, 0x00, 0x12, 0x00, 0x00, 0xff,    //16
          0xf0 };                                                                                            //01
        networkBufAdd(lineMsg, 54);
        break;
      }
# if 0 //LUDO: DEBUG
    default :
      fprintf(stdout, "%s %d: skip char %x\n", __FILE__, __LINE__, inputChr);
      break;
# endif
  }
}

  void 
handleDont(char inputChr)
{
  switch(inputChr)
  {
    case 0x22: //Linemode
      {
        char wontMsg[3] = { 0xff, 0xfc, 0x22 };  //wont linemode
        networkBufAdd(wontMsg, 3);
        break;
      }
# if 0 //LUDO: DEBUG
    default :
      fprintf(stdout, "%s %d: skip char %x\n", __FILE__, __LINE__, inputChr);
      break;
# endif
  }
}

  static void 
setDisplayAttribute(int attr)
{
  switch(attr)
  {
    case 0:     //Reset all attributes
      loc_curr_fg_color = COLOR_WHITE;
      loc_curr_bg_color = COLOR_BLACK;
      loc_curr_reverse = false;
      loc_curr_brightness = BRIGHTNESS_NORMAL;
      loc_curr_underlined = 0;
      loc_curr_blink = 0;
      break;
    case 1:     //Bright
      loc_curr_brightness = BRIGHTNESS_BRIGHT;
      break;
    case 2:     //Dim
      loc_curr_brightness = BRIGHTNESS_DIM;
      break;
    case 4:     //Underlined  
      loc_curr_underlined = 1;
      break;
    case 5:     //Blink
      loc_curr_blink = 1;
      break;
    case 8:     //Hidden
      break;
    case 7:     //Reverse
      loc_curr_reverse = true;
      break;

      //    Foreground Colours
    case 30:    //Black
      loc_curr_fg_color = COLOR_BLACK;
      break;
    case 31:    //Red
      loc_curr_fg_color = COLOR_RED;
      break;
    case 32:    //Green
      loc_curr_fg_color = COLOR_GREEN;
      break;
    case 33:    //Yellow
      loc_curr_fg_color = COLOR_YELLOW;
      break;
    case 34:    //Blue
      loc_curr_fg_color = COLOR_BLUE;
      break;
    case 35:    //Magenta
      loc_curr_fg_color = COLOR_MAGENTA;
      break;
    case 36:    //Cyan
      loc_curr_fg_color = COLOR_CYAN;
      break;
    case 37:    //White
      loc_curr_fg_color = COLOR_WHITE;
      break;

      //    Background Colours
    case 40:    //Black
      loc_curr_bg_color = COLOR_BLACK;
      break;
    case 41:    //Red
      loc_curr_bg_color = COLOR_RED;
      break;
    case 42:    //Green
      loc_curr_bg_color = COLOR_GREEN;
      break;
    case 43:    //Yellow
      loc_curr_bg_color = COLOR_YELLOW;
      break;
    case 44:    //Blue
      loc_curr_bg_color = COLOR_BLUE;
      break;
    case 45:    //Magenta
      loc_curr_bg_color = COLOR_MAGENTA;
      break;
    case 46:    //Cyan
      loc_curr_bg_color = COLOR_CYAN;
      break;
    case 47:    //White
      loc_curr_bg_color = COLOR_WHITE;
      break;
# if 0 //LUDO: DEBUG
    default :
      fprintf(stdout, "%s %d: skip attr %x\n", __FILE__, __LINE__, attr);
      break;
# endif
  }
}

  static void
renderGoBy(int x, int y)
{
  renderGoByReal(x, y, false);
}

  static void
psp_vt100_clear_screen(u8 c)
{
  int x;
  int y;
  for (y = 0; y < VT100_SCREEN_HEIGHT; y++) {
    for (x = 0; x < VT100_SCREEN_WIDTH; x++) {
      vt100_elem_t *elem = &vt100_screen[VT100_SCREEN_TOP_Y + y][x];
      elem->m_char = c;
      elem->m_font = loc_curr_font;
      elem->m_reverse = loc_curr_reverse;
      elem->m_brightness = loc_curr_brightness;
      elem->m_underlined = loc_curr_underlined;
      elem->m_blink      = loc_curr_blink;
      elem->bg_color = loc_curr_bg_color;
      elem->fg_color = loc_curr_fg_color;
    }
  }
# if 0 //LUDO: /* Needed ? */
  loc_curr_x = 0;
  loc_curr_y = 0;
# endif
}

  static void
psp_vt100_clear_line_to_bottom()
{
  int x;
  int y;
  y = loc_curr_y;
  while (y < VT100_SCREEN_HEIGHT) {
    psp_vt100_clear_line(y);
    y++;
  }
}

  static void
psp_vt100_clear_to_sol()
{
  int x;
  int y;
  x = loc_curr_x;
  y = loc_curr_y;
  while (x >= 0) {
    vt100_elem_t *elem = &vt100_screen[VT100_SCREEN_TOP_Y + y][x];
    elem->m_char = ' ';
    elem->m_font = loc_curr_font;
    elem->m_reverse = loc_curr_reverse;
    elem->m_brightness = loc_curr_brightness;
    elem->m_underlined = loc_curr_underlined;
    elem->m_blink = loc_curr_blink;
    elem->bg_color = loc_curr_bg_color;
    elem->fg_color = loc_curr_fg_color;
    x--;
  }
}

  static void
psp_vt100_clear_to_eol()
{
  int x;
  int y;
  x = loc_curr_x;
  y = loc_curr_y;
  while (x < VT100_SCREEN_WIDTH) {
    vt100_elem_t *elem = &vt100_screen[VT100_SCREEN_TOP_Y + y][x];
    elem->m_char = ' ';
    elem->m_font = loc_curr_font;
    elem->m_reverse = loc_curr_reverse;
    elem->m_brightness = loc_curr_brightness;
    elem->m_underlined = loc_curr_underlined;
    elem->m_blink = loc_curr_blink;
    elem->bg_color = loc_curr_bg_color;
    elem->fg_color = loc_curr_fg_color;
    x++;
  }
}

  static void
psp_vt100_clear_line_to_top()
{
  int x;
  int y;
  y = loc_curr_y;
  while (y >= 0) {
    psp_vt100_clear_line(y);
    y--;
  }
}

  static void 
renderSetRegion(int start, int end)
{
  //Set the current render area
  if (start >= 0) {
    loc_top_y = start;
  } else {
    start = loc_top_y;
  }
  if ((end > start) && ((end - start) < VT100_SCREEN_WIDTH)) {
    loc_bottom_y = end;
  } else {
    loc_bottom_y = VT100_SCREEN_HEIGHT - loc_top_y - 1;
  }
  loc_curr_x = 0;
  loc_curr_y = start;
}

  void 
handleEscSequence(char inputChr)
{
  switch(inputChr)
  {
    case 'H':  //goto possition
    case 'f':
      {
        char* ret;
        int x = 0;
        int y = 0;

        //sscanf(escSequence, "%d ; %d", &y, &x);
        y = strtol(escSequence, &ret, 10);
        if (ret != escSequence && *ret != '\0') //read something
        {
          ret++;  //hopefully we skip a ';'
          x = strtol(ret, NULL, 10);
        }
        else
          y = 0;

        //if they are numbers, we need to -- them
        if (x >= 1) x--;
        if (y >= 1) y--;

        flushBuffer();
        renderGoto(x,y);
        break;
      }
    case 'A':  //Move up by X
      {
        int moves = 1;
        moves = atoi(escSequence);
        if (moves == 0) moves = 1;
        flushBuffer();
        renderGoBy(0, -moves);
        break;
      }
    case 'B':  //Move down by X
      {
        int moves = 1;
        moves = atoi(escSequence);
        if (moves == 0) moves = 1;
        flushBuffer();
        renderGoBy(0, moves);
        break;
      }
    case 'C':  //Move Right by X
      {
        int moves = 1;
        moves = atoi(escSequence);
        if (moves == 0) moves = 1;
        flushBuffer();
        renderGoBy(moves, 0);
        break;
      }
    case 'D':  //Move left by X
      {
        int moves = 1;
        moves = atoi(escSequence);
        if (moves == 0) moves = 1;
        flushBuffer();
        renderGoBy(-moves, 0);
        break;
      }
    case 's':  //Save position
      flushBuffer();
      loc_saved_x = loc_curr_x;
      loc_saved_y = loc_curr_y;
      break;
    case 'u':  //Load Position
      flushBuffer();
      loc_curr_x = loc_saved_x;
      loc_curr_y = loc_saved_y;
      break;

    case 'K':
      switch(escSequence[0])
      {
        case '\0':
        case '0':  //Erase to end of line
          flushBuffer();
          psp_vt100_clear_to_eol();
          break;
        case '1':  //Erase to start of line
          flushBuffer();
          psp_vt100_clear_to_sol();
          break;
        case '2':  //Erase current line
          flushBuffer();
          psp_vt100_clear_line(loc_curr_y);
          break;
# if 0 //LUDO: DEBUG
        default :
          fprintf(stdout, "%s %d: skip escape %x\n", __FILE__, __LINE__, escSequence[0]);
          break;
# endif
      }
      break;
    case 'J':
      switch(escSequence[0])
      {
        case '\0':
        case '0':  //Erase current line to bottom
          flushBuffer();
          psp_vt100_clear_to_eol();
          renderGoBy(0,1);
          psp_vt100_clear_line_to_bottom();
          renderGoBy(0,-1);
          break;
        case '1':  //Erase current line to top
          flushBuffer();
          psp_vt100_clear_to_sol();
          renderGoBy(0,-1);
          psp_vt100_clear_line_to_top();
          renderGoBy(0,1);
          break;
        case '2':  //Erase screen
          flushBuffer();
          psp_vt100_clear_screen(' ');
          break;
# if 0 //LUDO: DEBUG
        default :
          fprintf(stdout, "%s %d: skip escape %x\n", __FILE__, __LINE__, escSequence[0]);
          break;
# endif
      }
      break;
    case 'm':  //Display Attributes
      {
        flushBuffer();
        int attr = 0;
        int a;
        bool setone = true;  //(true so we process a ^[m as a ^[0m, which it is)
        for (a=0; a < escSequenceLen; a++)
        {
          if (escSequence[a] >= '0' && escSequence[a] <= '9')
          {
            attr *= 10;
            attr += (escSequence[a]-'0');
            setone = true;
          }
          else
          {
            if (setone)
              setDisplayAttribute(attr);
            setone = false;
            attr = 0;
          }
        }
        if (setone)
          setDisplayAttribute(attr);
        break;
      }
    case 'h':  //Some setup things
      {
        if (escSequenceLen == 2)
          switch(escSequence[1])
          {
            case '1':  //cursor keys in applications mode
              cursorMode = APPLICATION_MODE;
              break;
            case '5':  //black chars on white BG (TODO should this change bold etc)
              loc_curr_fg_color = COLOR_BLACK;
              loc_curr_bg_color   = COLOR_WHITE;
              break;
            case '6':  //turn on region - origin mode  (TODO i dont actually know what this is ment to do, so ill fullscreen it)
              flushBuffer();
              renderSetRegion(VT100_DEFAULT_TOP_Y, VT100_DEFAULT_BOTTOM_Y);
              break;
            case '7':
              loc_autowrap = 1;
              break;
# if 0 //LUDO: DEBUG
            default :
              fprintf(stdout, "%s %d: skip escape %x\n", __FILE__, __LINE__, escSequence[1]);
              break;
# endif
          }
        break;
      }
    case 'l':  //Some un-setup things
      {
        if (escSequenceLen == 2)
          switch(escSequence[1])
          {
            case '1':  //cursor keys in cursor positioning mode
              cursorMode = POSITIONING_MODE;
              break;
            case '5':  //white chars on black BG (TODO should this change bold etc)
              loc_curr_fg_color = COLOR_WHITE;
              loc_curr_bg_color   = COLOR_BLACK;
              break;
            case '6':  //turn on region - full screen mode
              flushBuffer();
              renderSetRegion(VT100_DEFAULT_TOP_Y, VT100_DEFAULT_BOTTOM_Y);
              break;
            case '7': // reset autowrap
              loc_autowrap = 0;
              break;
# if 0 //LUDO: DEBUG
            default :
              /*
                 setcol DECCOLM        Set number of columns to 132           ^[[?3h
                 setsmooth DECSCLM     Set smooth scrolling                   ^[[?4h
                 setrevscrn DECSCNM    Set reverse video on screen            ^[[?5h
                 setorgrel DECOM       Set origin to relative                 ^[[?6h
                 setwrap DECAWM        Set auto-wrap mode                     ^[[?7h
                 setrep DECARM         Set auto-repeat mode                   ^[[?8h
                 setinter DECINLM      Set interlacing mode                   ^[[?9h
                 */
              fprintf(stdout, "%s %d: skip escape %x\n", __FILE__, __LINE__, escSequence[1]);
              break;
# endif
          }
        break;
      }
    case 'c':  //tell me your terminal type / status
      {
        //     *  ESC [ c           request to identify terminal type
        //     *  ESC [ 0 c         request to identify terminal type
        //     *  ESC Z             request to identify terminal type
        //Always assume its the same thing, send back a report saying we are vt100
        char *tosend = "\033[?1;0c";
        psp_ssh_put_data_to_ssh(tosend, 7);
        break;
      }
    case 'r':  //set a scroll region
      {
        //   "12;13"
        int start = VT100_DEFAULT_TOP_Y +1;
        int end = VT100_DEFAULT_BOTTOM_Y +1;
        //      sscanf(escSequence, "%d ; %d", &start, &end);
        char* ret;
        start = strtol(escSequence, &ret, 10);
        if (ret != escSequence && *ret != '\0') //read something
        {
          ret++;  //hopefully we skip a ';'
          end = strtol(ret, NULL, 10);
        }
        else  //if we didnt read anything, we need to reset start to its original value
        {
          start = VT100_DEFAULT_TOP_Y +1;
        }

        start--; end--;

        flushBuffer();
        renderSetRegion(start, end);
        break;
      }
      /*    HACK TODO - part of 'ansi' terminal, BitchX borks hard when on ansi and this is part of an almost fix.
            however! xterm and putty bork with BitchX in ansi mode, so we just wont go there ;)
            case 'S':  //cursor down - (TODO) at bottom of region, scroll up
            loc_state = 0;
            flushBuffer();
            renderScroll(1);
      //    renderGoByMove(0, 1);
      break;*/
  }
}

  static void
renderSetFont(int new_font)
{
  loc_curr_font = new_font;
}

  void 
psp_vt100_process(char* inputStr, int length)
{
# if 0 //LUDO: DEBUG
  {
    int index;
    for (index = 0; index < length; index++) {
      fprintf(stdout, "[%d] %x '%c'\n", index, 
          inputStr[index], (inputStr[index] >= ' ') ? inputStr[index] : 'X');
    }
  }
# endif
  torender[0] = '\0';

  int a;
  for (a = 0; a < length; a++)
  {
    switch (inputStr[a])  //need to handle esc chars first, so they arent picked up in an esc seq
    {
      case '\n':
      case '\r':
      case '\v':  //(vertical tab)
        {
          strncat(torender, &inputStr[a], 1);
          break;
        }
      case '\b':
        //flush
        flushBuffer();
        //move back 1
        renderGoBy(-1, 0);
        break;
        //    case '\000':  //According to a capture from bitchx in ansi mode, this may be newline too :|
        //Not so sure anymore, might remove this later...
        //      strncat(torender, "\n", 1);
        break;
      case 14:  // 'shift out'  Switch to alt font G1
        flushBuffer();
        using_gX = 1;
        renderSetFont(G1_font);
        break;
      case 15:  // 'shift in' Switch to normal font G0
        flushBuffer();
        using_gX = 0;
        renderSetFont(G0_font);
        break;

      default:
        switch (loc_state)
        {
          case 0:
            switch (inputStr[a])
            {
              case '\033': //Escape Character
                loc_state = 1;
                break;
              case '\377': // 255 (before a will/wont)
                loc_state = 3;
                break;
              default:
                strncat(torender, &inputStr[a], 1);
            }
            break;
          case 1:
            switch (inputStr[a])
            {
              case '[':
                loc_state = 2;
                break;
              case '#':
                loc_state = 10;
                break;
              case '7':  //save cursor pos
                loc_state = 0;
                flushBuffer();
                loc_saved_x = loc_curr_x;
                loc_saved_y = loc_curr_y;
                //also save some extras
                loc_saved_fg_color   = loc_curr_fg_color;
                loc_saved_bg_color   = loc_curr_bg_color;
                loc_saved_reverse    = loc_curr_reverse;
                loc_saved_brightness = loc_curr_brightness;
                loc_saved_underlined = loc_curr_underlined;
                loc_saved_blink      = loc_curr_blink;
                saved_G0_font = G0_font;
                saved_G1_font = G1_font;
                saved_using_gX = using_gX;
                break;
              case '8': //restore cursor pos
                loc_state = 0;
                flushBuffer();
                //send load msg
                loc_curr_x = loc_saved_x;
                loc_curr_y = loc_saved_y;
                //also load some extras
                loc_curr_fg_color = loc_saved_fg_color;
                loc_curr_bg_color = loc_saved_bg_color;
                loc_curr_reverse = loc_saved_reverse;
                loc_curr_brightness = loc_saved_brightness;
                loc_curr_underlined = loc_saved_underlined;
                loc_curr_blink      = loc_saved_blink;
                G0_font     = saved_G0_font;
                G1_font     = saved_G1_font;
                using_gX    = saved_using_gX;

                if (using_gX == 0) {
                  loc_curr_font = G0_font;
                } else {
                  loc_curr_font = G1_font;
                }
                break;
              case 'E':  //New line
                loc_state = 0;
                strncat(torender, "\r\n", 2);
                break;
              case 'D':  //cursor down - (TODO) at bottom of region, scroll up
                loc_state = 0;
                flushBuffer();
                renderGoByMove(0, 1);
                break;
              case 'M':  //cursor up - (TODO) at top of region, scroll down
                loc_state = 0;
                flushBuffer();
                renderGoByMove(0, -1);
                break;

              case '>':  //keypad keys in numeric mode *shrug*
              case '=':  //keypad keys in applications mode *shrug*
                loc_state = 0;
                break;
              case '(':
                loc_state = 11;
                break;
              case ')':
                loc_state = 12;
                break;
# if 0 //LUDO: DEBUG
              default :
                fprintf(stdout, "%s %d: skip char %x\n", __FILE__, __LINE__, inputStr[a]);
                break;
# endif
            }
            break;
          case 2:
            //End of Esc Sequence
            if ((inputStr[a] >= 'a' && inputStr[a] <= 'z') || (inputStr[a] >= 'A' && inputStr[a] <= 'Z'))
            {
              escSequence[escSequenceLen] = '\0';
              loc_state = 0;

              handleEscSequence(inputStr[a]);

              //Processed
              escSequenceLen = 0;
            }
            else
            {
              escSequence[escSequenceLen] = inputStr[a];
              escSequenceLen++;
            }
            break;
          case 3:
            switch(inputStr[a])
            {
              case '\373': //WILL
                loc_state = 4;
                break;
              case '\374': //WON'T
                loc_state = 5;
                break;
              case '\375': //DO
                loc_state = 6;
                break;
              case '\376': //DON'T
                loc_state = 7;
                break;
              case '\372': //suboption begin
                loc_state = 8;
                escSequence[0] = '\0';
                escSequenceLen = 0;
                break;
# if 0 //LUDO: DEBUG
              default :
                fprintf(stdout, "%s %d: skip char %x\n", __FILE__, __LINE__, inputStr[a]);
                break;
# endif
            }
            break;
          case 4: //WILL
            loc_state = 0;
            handleWill(inputStr[a]);
            break;
          case 5: //WON'T
            loc_state = 0;
            handleWont(inputStr[a]);
            break;
          case 6: //DO
            loc_state = 0; //Assume we can reset the loc_state.
            handleDo(inputStr[a]);
            break;
          case 7: //DON'T
            loc_state = 0;
            handleDont(inputStr[a]);
            break;
          case 8: //In Suboption
            handleSuboption(inputStr[a]);
            break;
          case 9: //Suboption escaped thing
            if (inputStr[a] == '\360') //end char
            {
              loc_state = 0;
              handleSuboptionEnd();
            }
            break;
          case 10:
            {
              int q;
              switch(inputStr[a])
              {
                case '8':  //Fill screen with E OMGOES
                  flushBuffer();
                  psp_vt100_clear_screen('E');
                  renderGoto(0,0);
                  break;
# if 0 //LUDO: DEBUG
                default :
                  fprintf(stdout, "%s %d: skip char %x\n", __FILE__, __LINE__, inputStr[a]);
                  break;
# endif
              }
              loc_state = 0;
              break;
            }
          case 11: // ^(X
            loc_state = 0;
            if (inputStr[a] == 'A' || inputStr[a] == 'B')
              G0_font = FONT_DEFAULT;
            else if (inputStr[a] == '0')
              G0_font = FONT_LINEDRAW;

            if (using_gX == 0) {
              flushBuffer();
              loc_curr_font = G0_font;
            }
            break;
          case 12: // ^)X
            loc_state = 0;
            if (inputStr[a] == 'A' || inputStr[a] == 'B')
              G1_font = FONT_DEFAULT;
            else if (inputStr[a] == '0')
              G1_font = FONT_LINEDRAW;

            if (using_gX == 1)
            {
              flushBuffer();
              loc_curr_font = G1_font;
            }
            break;


            //Else who knows what happened, assume its a fuckup and do nothing....
        }

    }
    //The great big debug hack lol
    /*    {
          char aaa[10];
          sprintf(aaa, "%d(%d) ", inputStr[a], loc_state);
          strcat(torender, aaa);
          }  */
  }

  //FLUSH
  flushBuffer();

  if (networkBufSize != 0) {
    psp_ssh_put_data_to_ssh(networkBuf, networkBufSize);
    networkBufSize = 0;
  }
}

  void
psp_vt100_screen_scroll_up()
{
  vt100_display_from_y -= VT100_SCREEN_HEIGHT / 2;
  if (vt100_display_from_y < 0) vt100_display_from_y = 0;
}

  void
psp_vt100_screen_scroll_down()
{
  vt100_display_from_y += VT100_SCREEN_HEIGHT / 2;
  if (vt100_display_from_y > VT100_SCREEN_TOP_Y) vt100_display_from_y = VT100_SCREEN_TOP_Y;
}

  void
psp_vt100_display_screen()
{
# if 0 //LUDO: FOR_DEBUG
  {
    char buffer[32];
    sprintf(buffer, "(%d %d)", loc_curr_x, loc_curr_y);
    int color = psp_sdl_rgb(0xff, 0xff, 0xff);
    psp_sdl_back_print(40, 6, buffer, color);
  }
# endif

  psp_sdl_select_font_6x10();

  int line = 0;
  int y_pix = 0;

  int first_line = vt100_display_from_y;
  int last_line  = vt100_display_from_y + VT100_SCREEN_HEIGHT;

  if (last_line >= VT100_MAX_LINE) {
    first_line = VT100_SCREEN_TOP_Y;
    last_line  = VT100_SCREEN_TOP_Y + VT100_SCREEN_HEIGHT - 1;
  }

  int abs_curr_y = loc_curr_y + VT100_SCREEN_TOP_Y;
  for (line = first_line; line <= last_line; line++) {
    vt100_elem_t *scan_line = &vt100_screen[line][0];
    int x_pix = 0;
    int x;
    for (x = 0; x < VT100_SCREEN_WIDTH; x++) {
      int bright = scan_line[x].m_brightness;
      int blink  = scan_line[x].m_blink;
      int underlined = scan_line[x].m_underlined;
      int fg_color = vt100_colors[scan_line[x].fg_color][bright];
      int bg_color = vt100_colors[scan_line[x].bg_color][bright];
      if (scan_line[x].m_reverse) {
        int swap_color = fg_color;
        fg_color = bg_color;
        bg_color = swap_color;
      }
      if (loc_visual_bell) {
        int swap_color = fg_color;
        fg_color = bg_color;
        bg_color = swap_color;
      }
      char m_char = scan_line[x].m_char;
      if (loc_visual_bell) {
        if (fg_color == bg_color) {
          fg_color = vt100_colors[loc_curr_bg_color][bright];
          bg_color = vt100_colors[loc_curr_fg_color][bright];
        }
        if (m_char < ' ') m_char = ' ';
      }
      if (blink && (loc_blink_counter < (BLINK_MAX_COUNTER/2))) {
        fg_color = bg_color;
      }
      psp_sdl_put_char(x_pix, y_pix, fg_color, bg_color, m_char, 1, 1);

      /* Display cursor */
      if ((abs_curr_y == line) && (x == loc_curr_x)) {
        psp_sdl_put_char(x_pix, y_pix, vt100_colors[loc_curr_fg_color][1], 0, '_', 1, 0);
      } else
        if (underlined) {
          psp_sdl_put_underline(x_pix, y_pix, fg_color);
        }
      x_pix += psp_font_width;
    }
    y_pix += psp_font_height;
  }
  if (loc_visual_bell > 0) loc_visual_bell--;
  loc_blink_counter = (loc_blink_counter + 1) % BLINK_MAX_COUNTER;

  psp_sdl_select_font_8x8();
}

  void
psp_vt100_init()
{
  int bright;
  int c;
  for (bright = 0; bright < 3; bright++) {
    if (bright == 0) c = 0xaF;
    else
      if (bright == 1) c = 0xFF;
      else             c = 0xcF;

    vt100_colors[COLOR_BLACK  ][bright] = psp_sdl_rgb(0x00, 0x00, 0x00);
    vt100_colors[COLOR_RED    ][bright] = psp_sdl_rgb(c   , 0x00, 0x00);
    vt100_colors[COLOR_GREEN  ][bright] = psp_sdl_rgb(0x00, c   , 0x00);
    vt100_colors[COLOR_BLUE   ][bright] = psp_sdl_rgb(0x00, 0x00, c   );
    vt100_colors[COLOR_YELLOW ][bright] = psp_sdl_rgb(c   , c   , 0x00);
    vt100_colors[COLOR_MAGENTA][bright] = psp_sdl_rgb(c   , c   , 0x00);
    vt100_colors[COLOR_CYAN   ][bright] = psp_sdl_rgb(0x00, c   , c   );
    vt100_colors[COLOR_WHITE  ][bright] = psp_sdl_rgb(c   , c   , c   );
  }

  loc_saved_x = loc_curr_x = 0;
  loc_saved_y = loc_curr_y = 0;

  loc_curr_font = 0;

  vt100_display_from_y = VT100_SCREEN_TOP_Y;
  memset(vt100_screen, 0, sizeof(vt100_screen));

# if 0 //LUDO: FOR_TEST
  wontEcho = false;
  cygwinHacksMode = false;

  loc_curr_fg_color = COLOR_WHITE;
  loc_curr_bg_color = COLOR_BLACK;
  loc_curr_reverse = false;
  loc_curr_brightness = BRIGHTNESS_NORMAL;

  G0_font = FONT_DEFAULT;
  G1_font = FONT_LINEDRAW;
  using_gX = 0;

  loc_saved_fg_color = 0;
  loc_saved_bg_color = 0;
  loc_saved_reverse = 0;
  loc_saved_brightness = 0;

  saved_G0_font = 0;
  saved_G1_font = 0;
  saved_using_gX = 0;

  loc_top_y    = VT100_DEFAULT_TOP_Y;
  loc_bottom_y = VT100_DEFAULT_BOTTOM_Y;

  loc_autowrap = 0;
  networkBufSize = 0;
# endif

# if 0 //LUDO: FOR_DEBUG
  int x;
  int y;
  for (y = 0; y < VT100_MAX_LINE; y++) {
    char buffer[32];
    sprintf(buffer, "line %d", y);
    int l = strlen(buffer);
    for (x = 0; x < VT100_SCREEN_WIDTH; x++) {
      vt100_elem_t *elem = &vt100_screen[y][x];
      if (x < l) elem->m_char = buffer[x];
      else       elem->m_char = ' ';
      elem->m_font = loc_curr_font;
      elem->m_reverse = loc_curr_reverse;
      elem->m_brightness = loc_curr_brightness;
      elem->bg_color = loc_curr_bg_color;
      elem->fg_color = loc_curr_fg_color;
    }
  }
# endif
}

  void
psp_vt100_save_log(void)
{
  FILE* File;
  char  TmpFileName[MAX_PATH];
  int   y = 0;
  int   x = 0;
  char  v = 0;

  sprintf(TmpFileName,"%s/log/log_%d.txt", PSPSSH.psp_homedir, PSPSSH.psp_log_id++);
  if (PSPSSH.psp_log_id >= 10) PSPSSH.psp_log_id = 0;
  File = fopen(TmpFileName, "w");
  if (File) {
    for (y = 0; y < VT100_MAX_LINE; y++) {
      for (x = 0; x < VT100_SCREEN_WIDTH; x++) {
        vt100_elem_t *elem = &vt100_screen[y][x];
        if (elem->m_char) break;
      }
      if (x != VT100_SCREEN_WIDTH) break;
    }
    while (y < VT100_MAX_LINE) {
      for (x = 0; x < VT100_SCREEN_WIDTH; x++) {
        v = vt100_screen[y][x].m_char;
        if (v < ' ') v = ' ';
        fputc(v, File);
      }
      fputc('\n', File);
      y++;
    }
    fclose(File);
  }
}

